package code

import (
	"go/ast"
	"go/parser"
	"go/token"
	"strings"
)

// ExtractComponents converts ast file into code components model
func ExtractComponents(f *ast.File, opts ...Option) File {
	options := NewOptions(opts...)
	var file File
	file.PackageName = f.Name.Name

	for _, decl := range f.Decls {
		switch t := decl.(type) {
		case *ast.GenDecl:
			for _, spec := range t.Specs {
				switch spec := spec.(type) {
				case *ast.ImportSpec:
					if options.ExtraImports {
						file.Imports = append(file.Imports, ExtraImports(spec))
					}
				case *ast.TypeSpec:
					switch t := spec.Type.(type) {
					case *ast.StructType:
						if options.ExtraStructs {
							file.Structs = append(file.Structs, extractStructType(spec.Name.Name, file.PackageName, t))
						}
					case *ast.InterfaceType:
						if options.ExtraInterfaces {
							file.Interfaces = append(file.Interfaces, ExtractInterfaceSpec(spec, t))
						}
					}
				}
			}
		case *ast.FuncDecl:
			if options.ExtraFunctions {
				file.Methods = append(file.Methods, ExtractMethod(t))
			}
		}
	}
	return file
}

func ExtractComponentsByPath(componentPath string, opts ...Option) (*File, error) {
	filesSpec := File{
		PackageName: "",
		Imports:     make([]*Import, 0),
		Structs:     make([]*Struct, 0),
		Interfaces:  make([]*InterfaceSpec, 0),
	}

	fset := token.NewFileSet()
	ps, err := parser.ParseDir(fset, componentPath, nil, parser.ParseComments)
	if err != nil {
		return nil, err
	}
	for pName, pkg := range ps {
		if strings.HasSuffix(pName, "_test") || pName == "main" {
			continue
		}
		filesSpec.PackageName = pName
		for _, fi := range pkg.Files {
			f := ExtractComponents(fi, opts...)
			filesSpec.Imports = append(filesSpec.Imports, f.Imports...)
			filesSpec.Structs = append(filesSpec.Structs, f.Structs...)
			filesSpec.Interfaces = append(filesSpec.Interfaces, f.Interfaces...)
			filesSpec.Methods = append(filesSpec.Methods, f.Methods...)
		}
	}
	return &filesSpec, nil
}
